今天要來介紹的是表單的驗證,畢竟不可能讓每一份表單都交由後端來驗證並返還結果,往往都會由前端來排除最基本的使用者操作失誤,如說好密碼要大小寫與數字兼具,卻只給小寫加數字,又或者填寫日期時,結束日期在開始日期之前,諸如此類的問題。
當然這並不是說後端不用"再"檢查,該做的事情還是要做,只是前端先阻擋最基本的錯誤,可以省下許多的 http 請求。所以今天要帶大家透過資料註解來撰寫簡單的畫面驗證,並且客製化自己的驗證。
又稱驗證屬性(Attribute),可以讓你指定模型屬性(Property)的驗證規則。
以下介紹幾個比較常用的驗證屬性:
Required
: 該欄位為必填。
StringLength(int maximun)
: 該字串欄位長度限制,最小值是可選參數,建構式只要求最大值,以限制長度五到十的字串為例:
[StringLength(10, MinimumLength = 5)]
Range(int minimun, int maximun)
: 該數值欄位的範圍限制,以限制數字區間為一到兩百為例:
[Range(1, 200)]
RegularExpression(string pattern)
: 該欄位需符合特定的正規表達式,以簡單的台灣手機格式為例:
[RegularExpression(@"^09\d{8}$")]
EmailAddress
: 該欄位需為電子信箱格式。
Url
: 該欄位需為 URL 格式。
當然不只這些,想看完整的話可以到System.ComponentModel.DataAnnotations 命名空間觀看。
若內建的驗證屬性無法達成你的需求時,我們可以建立自訂驗證屬性,只需要繼承 ValidationAttribute
並且覆寫 IsValid
方法即可。
IsValid
方法有兩種多載:IsValid(object value)
與 IsValid(object value, ValidationContext validationContext)
value
該屬性的值。
validationContext
驗證的上下文,並可以透過 ObjectInstance
取得驗證實例後取得其他欄位的值。
而我們,只需要根據情境覆寫一個方法即可,若兩個都覆寫,則 IsValid(object value)
會是失效的那個。
客製化驗證通常都是針對不同欄位間對彼此的影響來決定驗證內容,而這邊就以一個簡單的例子說明,如果某欄位在某身分時必填,就可以寫成:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var user = (UserViewModel)validationContext.ObjectInstance;
if (user.UserType == UserType.member && (value is null || string.IsNullOrEmpty(value.ToString())))
{
return new ValidationResult(base.ErrorMessage ?? "此欄位會員必填!");
}
return ValidationResult.Success;
}
這邊要提一點,我回傳錯誤訊息時,使用了base.ErrorMessage ?? "此欄位會員必填!"
,讓外部使用時也能透過設定 ValidationAttribute
的 ErrorMessage
來客製化訊息,而所有的內建驗證屬性也都繼承於 ValidationAttribute
,所以都能夠使用 ErrorMessage
屬性來客製化錯誤訊息,也就是:
[Required(ErrorMessage = "此欄位為必填!")]
當你撰寫好客製化驗證後,會發現(可能吧?)在顯示錯誤訊息時,明明在 <ValidationSummary />
有正確顯示錯誤,而 <ValidationMessage For="@(()=>model.PropertyName)" />
卻一點訊息都沒有!
因為在傳回 ValidationResult
時,要指定是哪些屬性名稱的錯誤,否則就會對應不到,所以我們的傳回值應該是:
new ValidationResult(base.ErrorMessage ?? "此欄位會員必填!", new[] { validationContext.MemberName })
第二個參數是屬性名稱陣列,可以透過 validationContext.MemberName
來取得該屬性的名稱,若裡面放了別的屬性名稱,別的屬性就會出現同樣的錯誤,是不是很神奇呀?例如結束時間必需在開始時間之後這類訊息,往往都只能擺在一個屬性上,但其實對於使用者來說,調整兩個屬性的其中一個就好,也就是並非單一個錯誤,而是兩個都是錯誤才對吧?若想看此類驗證器,我有撰寫一個驗證器在範例程式碼 - day10。
最後一點是,IsValid(object value)
的傳回值是 bool
,而不是 ValidationResult
,所以並無上述問題,且 ValidationMessage
能正確取得該屬性名稱的錯誤訊息。
以上就是透過資料註解來進行資料驗證,與客製化驗證器的方法。
感謝大家的閱讀,我們明天見。